package com.base.cn.platform.os.common.utils.email;

import com.base.cn.platform.os.common.j2cache.CustomCacheChannel;
import com.base.cn.platform.os.common.j2cache.J2CacheRegion;
import com.base.cn.platform.os.common.utils.ObjectUtils;
import com.base.cn.platform.os.common.utils.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mail.javamail.JavaMailSenderImpl;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Component;

import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

/**
 * 邮箱发送工具
 * <p>主要用于发送验证码短信，及后台批量给学员发送邮件</p>
 * <p>每个发送验证码的方法都有一个对应的验证方法，验证成功后会自己删除验证码缓存</p>
 * <p>每个操作，在24小时内只能发10封邮件</p>
 * @author s.li
 * @create 2017-07-15-9:40
 */
@Component
public class EmailSendUtils {

    @Autowired
    private CustomCacheChannel customCacheChannel;

    private Map<String,String> emailConfigMap;
    private Map<String,String> infoConfigMap;


    private static Logger logger = LoggerFactory.getLogger(EmailSendUtils.class);
    private static JavaMailSenderImpl javaMailSender= new JavaMailSenderImpl();
    private static final String encoding = "UTF-8";
    private static final boolean multipart=true;
    private static final String protocol = "smtps";

    //单一操作-单个IP，24小时内最大发送量
    private static int MAX_SEND_NUM = 10;
    //一天的毫秒量
    private static int ONE_DAY = 1000*60*60*24;
    //一分钟毫秒量
    private static int ONE_MINUTE = 1000*60;

    //发送类型，1=注册
    private static final int SEND_TYPE_REG=1;
    //发送类型，2=找回密码
    private static final int SEND_TYPE_PWD=2;
    //发送类型，3=绑定邮箱号（用于验证新绑定的邮箱号）
    private static final int SEND_TYPE_BINDING=3;
    //发送类型，4=改绑邮箱号（用于验证原绑定的邮箱号）
    private static final int SEND_TYPE_UN_BINDING=4;

    //====================================================================
    /**
     * 发送注册验证码
     * @param email 邮箱号
     * @return true发送成功，false发送失败
     * @throws Exception
     */
    public boolean sendReg(String email){
        sendEmail(email,SEND_TYPE_REG);
        return true;
    }

    /**
     * 验证邮箱注册码真实性
     * @param email 手号
     * @param code 客户端输入的验证码
     * @return true验证通过，false验证失败
     */
    public boolean validateRegCode(String email,String code){
        String _cdoe = getRedisCode(email,SEND_TYPE_REG);
        if(!code.equals(_cdoe)){
            return false;
        }
        return true;
    }

    /**
     * 删除注册验证码的缓存
     * @param email
     */
    public void removeRegCode(String email){
        removeRedisEmailCode(email,SEND_TYPE_REG);
    }

    /**
     * 发送找回密码验证
     * @param email 邮箱号
     * @return true发送成功，false发送失败
     * @exception Exception
     */
    public boolean sendPwd(String email){
        sendEmail(email,SEND_TYPE_PWD);
        return true;
    }

    /**
     * 验证邮箱找回密码的验证码的真实性
     * @param email 手号
     * @param code 客户端输入的验证码
     * @return true验证通过，false验证失败
     */
    public boolean validatePwdCode(String email,String code){
        String _cdoe = getRedisCode(email,SEND_TYPE_PWD);
        if(!code.equals(_cdoe)){
            return false;
        }

        return true;
    }

    /**
     * 删除找回密码的邮箱验证缓存
     * @param email
     */
    public void removePwdCode(String email){
        removeRedisEmailCode(email, SEND_TYPE_PWD);
    }

    /**
     * 发送绑定邮箱号验证（用于验证新绑定的邮箱号）
     * @param email 邮箱号
     * @return true发送成功，false发送失败
     * @exception Exception
     */
    public boolean sendBinding(String email){
        sendEmail(email,SEND_TYPE_BINDING);
        return true;
    }

    /**
     * 验证邮箱号绑定的验证码的真实性（用于验证新绑定的邮箱号）
     * @param email 手号
     * @param code 客户端输入的验证码
     * @return true验证通过，false验证失败
     */
    public boolean validateBindingCode(String email,String code){
        String _cdoe = getRedisCode(email,SEND_TYPE_BINDING);
        if(!code.equals(_cdoe)){
            return false;
        }
        return true;
    }

    /**
     * 删除验证邮箱绑定验证的缓存（用于验证新绑定的邮箱号）
     * @param email
     */
    public void removeBindingCode(String email){
        removeRedisEmailCode(email, SEND_TYPE_BINDING);
    }

    /**
     * 发送改绑邮箱号验证 （用于验证原绑定邮箱号）
     * @param email 邮箱号
     * @return true发送成功，false发送失败
     * @exception Exception
     */
    public boolean sendUnBinding(String email){
        sendEmail(email,SEND_TYPE_UN_BINDING);
        return true;
    }

    /**
     * 验证邮箱号改绑的验证码的真实性（用于验证原绑定邮箱号）
     * @param email 邮箱号
     * @param code 客户端输入的验证码
     * @return true验证通过，false验证失败
     */
    public boolean validateUnBindingCode(String email,String code){
        String _cdoe = getRedisCode(email,SEND_TYPE_UN_BINDING);
        if(!code.equals(_cdoe)){
            return false;
        }
        return true;
    }

    /**
     * 删除验证邮箱号改绑的验证码的缓存 （用于验证原绑定邮箱号）
     * @param email
     */
    public void removeUnBindingCode(String email){
        removeRedisEmailCode(email, SEND_TYPE_UN_BINDING);
    }
    //====================================================================

    public boolean sendEmail(String email, int sendType){
        //安全验证
        if(!safetyVerification(email,sendType)){
            return false;
        }
        String code = StringUtils.getRandStr(6);
        String[] emails = {email};
        Map<String,String> contextTitle = getContextTitle(sendType,code);
        String title =contextTitle.get("title");
        String context =contextTitle.get("context");
        sendEmail(emails,context,title);
        setSafetyData(email,sendType,code);
        return true;
    }

    /**
     * 发送送件(支持多发)
     * @param emails 收件人邮箱号数组
     * @param context 邮件内容
     * @param title 邮件标题
     * @throws Exception
     */
    public void sendEmail(String[] emails, String context, String title) {
        init();
        try{
            MimeMessage mimeMessage = javaMailSender.createMimeMessage();
            MimeMessageHelper messageHelper = new MimeMessageHelper(mimeMessage, multipart, encoding);
            messageHelper.setFrom(new InternetAddress(javaMailSender.getUsername()));
            messageHelper.setSubject(title);
            messageHelper.setText(context,true);
            messageHelper.setTo(emails);
            mimeMessage = messageHelper.getMimeMessage();
            EmailThread et = new EmailThread(mimeMessage);
            et.start();
        }catch (Exception e){
            logger.error("sendEmail()--error",e);
        }

    }

    /**
     * 安全验证
     * @param email 邮箱号
     * @param type 操作类型
     * @return true验证通过，false验证未通过
     */
    private boolean safetyVerification(String email, int type){
        String commKey = getCommKey(email,type);

        //验证上次发送时间，判断是否频繁发送（1分钟内只能发送一次）
        String timeKey = commKey+"_every_time";
        //得到上次发送时间
        Date upTime = (Date) customCacheChannel.get(J2CacheRegion.ONE_MINUTE_REGION.getKey(), timeKey).getValue();
        if(ObjectUtils.isNotEmpty(upTime)){
            //如果上一次发送未起过一分钟，则视为频繁发送
            if((new Date().getTime()-upTime.getTime())< ONE_MINUTE){
                logger.info("+++++++++++++++++++++++++++++单一操作过于频繁发送");
                return false;
            }
        }

        //频繁发送验证通过，再验证单一操作24小时内的发送量
        String firsTimeKey = commKey+"_firs_time";
        Date firsTime = (Date) customCacheChannel.get(J2CacheRegion.ONE_DAY_REGION.getKey(), firsTimeKey).getValue();
        //如果有数据
        if(ObjectUtils.isNotEmpty(firsTime)){
            //如果上一次的同一个操作第一次发送未超过24小时
            if((new Date().getTime()-firsTime.getTime()) < ONE_DAY){
                String numKey = commKey+"_num";//发送量的Key
                Integer sendNum = (Integer) customCacheChannel.get(J2CacheRegion.ONE_DAY_REGION.getKey(), numKey).getValue();
                if(ObjectUtils.isNotEmpty(sendNum)){
                    //如果在24小时内，单一操作量已超过最大量，则验证不通过
                    if(sendNum.intValue()>= MAX_SEND_NUM){
                        logger.info("----------------------------单一操作24小时内邮件发送量已达到最大量");
                        return false;
                    }
                }
            }
        }
        return true;
    }

    /**
     * 设置安全验证数据
     * @param email 邮箱号
     * @param type 发送类型
     * @param code 验证码
     */
    private void setSafetyData(String email,int type,String code){
        String commKey = getCommKey(email,type);

        //设置每次发送的时间点
        String everyTimeKey = commKey+"_every_time";
        customCacheChannel.set(J2CacheRegion.ONE_MINUTE_REGION.getKey(), everyTimeKey, new Date());

        //设置每天第一次发送的时间点
        String firsTimeKey = commKey+"_firs_time";
        Date firsTime = (Date) customCacheChannel.get(J2CacheRegion.ONE_DAY_REGION.getKey(), firsTimeKey).getValue();
        if(ObjectUtils.isNotEmpty(firsTime)){
            //如果超过24小时了，重新设置时间点
            if((new Date().getTime()-firsTime.getTime()) >= ONE_DAY){
                customCacheChannel.set(J2CacheRegion.ONE_DAY_REGION.getKey(), firsTimeKey, new Date());
            }
        }else{
            customCacheChannel.set(J2CacheRegion.ONE_DAY_REGION.getKey(), firsTimeKey, new Date());
        }

        //设置已发送的量，每天同一个操作发送短信量有限制
        String numKey = commKey+"_num";//发送量的Key
        Integer sendNum = (Integer) customCacheChannel.get(J2CacheRegion.ONE_DAY_REGION.getKey(), numKey).getValue();
        if(!ObjectUtils.isNotEmpty(sendNum)){
            sendNum = 1;
        }else{
            sendNum = sendNum.intValue()+1;
        }
        logger.info("------------------email send num :"+sendNum);
        customCacheChannel.set(J2CacheRegion.ONE_DAY_REGION.getKey(), numKey,sendNum);
        //缓存验证码
        customCacheChannel.set(J2CacheRegion.THIRTY_MINUTE_REGION.getKey(), commKey,code);
    }

    /**
     * 获取通过的缓存Key
     * @param email
     * @param type
     * @return
     */
    private String getCommKey(String email,int type){
        String emailUsername = emailConfigMap.get("emailUsername");
        return emailUsername.replaceAll("@","_").replaceAll("\\.","_")+email.replaceAll("@","_").replaceAll("\\.","_")+"_"+type;
    }

    /**
     * 删除验证码缓存，用于删除验证成功之后的数据
     * @param email 邮箱号
     * @param sendTypeReg 发送类型
     */
    private void removeRedisEmailCode(String email, int sendTypeReg) {
        String commKey = getCommKey(email,sendTypeReg);
        customCacheChannel.evict(J2CacheRegion.THIRTY_MINUTE_REGION.getKey(),commKey);
    }

    /**
     * 获取缓存中的验证码
     * @param email 邮箱号
     * @param sendType 操作类型
     * @return 验证码
     */
    private String getRedisCode(String email,int sendType){
        String commKey = getCommKey(email,sendType);
        String _code = customCacheChannel.get(J2CacheRegion.THIRTY_MINUTE_REGION.getKey(), commKey).asString();
        return _code;
    }

    private Map<String,String> getContextTitle(int type, String code){
        String company = infoConfigMap.get("company");
        String context="";
        String title = "";
        Map<String,String> resultMap = new HashMap<>();
        switch (type){
            case 1:
                context = "【注册】您的注册验证码："+code+"，有效时间24小时间，如果非本人邮箱请忽略。——【"+company+"】";
                title="注册验证";
                break;
            case 2:
                context = "【找回密码】您的验证码："+code+"，有效时间24小时间，如果非本人邮箱请忽略。——【"+company+"】";
                title="找回密码验证";
                break;
            case 3:
                context = "【更换邮箱】您的验证码："+code+"，有效时间24小时间，如果非本人邮箱请忽略。——【"+company+"】";
                title="改绑定验证";
                break;
            case 4:
                context ="【绑定邮箱】您的验证码："+code+"，有效时间24小时间，如果非本人邮箱请忽略。——【"+company+"】";
                title="绑定验证";
                break;
            default:
        }
        resultMap.put("title",title);
        resultMap.put("context",context);
        return resultMap;
    }


    //==================================================================================
    public void setEmailConfig(Map<String,String> emailConfigMap){
        this.emailConfigMap = emailConfigMap;
    }
    public void setInfoConfigMap(Map<String,String> infoConfigMap){
        this.infoConfigMap = infoConfigMap;
    }

    /**
     * 内部类发送邮件线程
     * @ClassName EmailThread
     * @package com.fairyhawk.common.service
     * @description
     */
    class EmailThread extends Thread {
        private final MimeMessage mimeMessage;
        public EmailThread(MimeMessage mimeMessage) {
            this.mimeMessage = mimeMessage;
        }
        @Override
        public void run() {
            javaMailSender.send(mimeMessage);
        }

    }

    /**
     * 初始化服务邮箱号
     */
    private void init(){
        String emailPassword = emailConfigMap.get("emailPassword");
        String emailHost = emailConfigMap.get("emailHost");
        String emailUsername = emailConfigMap.get("emailUsername");
        String port = emailConfigMap.get("port");
        javaMailSender.setHost(emailHost);
        javaMailSender.setPassword(emailPassword);
        javaMailSender.setUsername(emailUsername);
        javaMailSender.setDefaultEncoding(encoding);
        javaMailSender.setProtocol(protocol);
        Properties properties = javaMailSender.getJavaMailProperties();
        properties.put("mail.smtp.auth",true);
        javaMailSender.setJavaMailProperties(properties);

        if(StringUtils.isNotEmpty(port)){
            javaMailSender.setPort(Integer.parseInt(port));
        }
    }
}
